// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_CAST_LOGGING_STATS_EVENT_SUBSCRIBER_H_
#define MEDIA_CAST_LOGGING_STATS_EVENT_SUBSCRIBER_H_

#include <stddef.h>
#include <stdint.h>

#include <memory>

#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/linked_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/time/tick_clock.h"
#include "media/cast/logging/logging_defines.h"
#include "media/cast/logging/raw_event_subscriber.h"
#include "media/cast/logging/receiver_time_offset_estimator.h"

namespace base {
class DictionaryValue;
class ListValue;
}

namespace media {
namespace cast {

    class StatsEventSubscriberTest;

    // A RawEventSubscriber implementation that subscribes to events,
    // and aggregates them into stats.
    class StatsEventSubscriber : public RawEventSubscriber {
    public:
        StatsEventSubscriber(EventMediaType event_media_type,
            base::TickClock* clock,
            ReceiverTimeOffsetEstimator* offset_estimator);

        ~StatsEventSubscriber() final;

        // RawReventSubscriber implementations.
        void OnReceiveFrameEvent(const FrameEvent& frame_event) final;
        void OnReceivePacketEvent(const PacketEvent& packet_event) final;

        // Returns stats as a DictionaryValue. The dictionary contains one entry -
        // "audio" or "video" pointing to an inner dictionary.
        // The inner dictionary consists of string - double entries, where the string
        // describes the name of the stat, and the double describes
        // the value of the stat. See CastStat and StatsMap below.
        std::unique_ptr<base::DictionaryValue> GetStats() const;

        // Resets stats in this object.
        void Reset();

    private:
        friend class StatsEventSubscriberTest;
        FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, EmptyStats);
        FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, CaptureEncode);
        FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Encode);
        FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Decode);
        FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, PlayoutDelay);
        FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, E2ELatency);
        FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Packets);
        FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Histograms);

        static const size_t kMaxFrameInfoMapSize = 100;

        // Generic statistics given the raw data. More specific data (e.g. frame rate
        // and bit rate) can be computed given the basic metrics.
        // Some of the metrics will only be set when applicable, e.g. delay and size.
        struct FrameLogStats {
            FrameLogStats();
            ~FrameLogStats();
            int event_counter;
            size_t sum_size;
            base::TimeDelta sum_delay;
        };

        struct PacketLogStats {
            PacketLogStats();
            ~PacketLogStats();
            int event_counter;
            size_t sum_size;
        };

        class SimpleHistogram {
        public:
            // This will create N+2 buckets where N = (max - min) / width:
            // Underflow bucket: < min
            // Bucket 0: [min, min + width - 1]
            // Bucket 1: [min + width, min + 2 * width - 1]
            // ...
            // Bucket N-1: [max - width, max - 1]
            // Overflow bucket: >= max
            // |min| must be less than |max|.
            // |width| must divide |max - min| evenly.
            SimpleHistogram(int64_t min, int64_t max, int64_t width);

            ~SimpleHistogram();

            void Add(int64_t sample);

            void Reset();

            std::unique_ptr<base::ListValue> GetHistogram() const;

        private:
            int64_t min_;
            int64_t max_;
            int64_t width_;
            std::vector<int> buckets_;
        };

        enum CastStat {
            // Capture frame rate.
            CAPTURE_FPS,
            // Encode frame rate.
            ENCODE_FPS,
            // Decode frame rate.
            DECODE_FPS,
            // Average capture latency in milliseconds.
            AVG_CAPTURE_LATENCY_MS,
            // Average encode duration in milliseconds.
            AVG_ENCODE_TIME_MS,
            // Duration from when a frame is encoded to when the packet is first
            // sent.
            AVG_QUEUEING_LATENCY_MS,
            // Duration from when a packet is transmitted to when it is received.
            // This measures latency from sender to receiver.
            AVG_NETWORK_LATENCY_MS,
            // Duration from when a frame is encoded to when the packet is first
            // received.
            AVG_PACKET_LATENCY_MS,
            // Average latency between frame encoded and the moment when the frame
            // is fully received.
            AVG_FRAME_LATENCY_MS,
            // Duration from when a frame is captured to when it should be played out.
            AVG_E2E_LATENCY_MS,
            // Encode bitrate in kbps.
            ENCODE_KBPS,
            // Packet transmission bitrate in kbps.
            TRANSMISSION_KBPS,
            // Packet retransmission bitrate in kbps.
            RETRANSMISSION_KBPS,
            // Duration in milliseconds since last receiver response.
            MS_SINCE_LAST_RECEIVER_RESPONSE,
            // Number of frames captured.
            NUM_FRAMES_CAPTURED,
            // Number of frames dropped by encoder.
            NUM_FRAMES_DROPPED_BY_ENCODER,
            // Number of late frames.
            NUM_FRAMES_LATE,
            // Number of packets that were sent (not retransmitted).
            NUM_PACKETS_SENT,
            // Number of packets that were retransmitted.
            NUM_PACKETS_RETRANSMITTED,
            // Number of packets that were received by receiver.
            NUM_PACKETS_RECEIVED,
            // Number of packets that had their retransmission cancelled.
            NUM_PACKETS_RTX_REJECTED,
            // Unix time in milliseconds of first event since reset.
            FIRST_EVENT_TIME_MS,
            // Unix time in milliseconds of last event since reset.
            LAST_EVENT_TIME_MS,

            // Histograms
            CAPTURE_LATENCY_MS_HISTO,
            ENCODE_TIME_MS_HISTO,
            QUEUEING_LATENCY_MS_HISTO,
            NETWORK_LATENCY_MS_HISTO,
            PACKET_LATENCY_MS_HISTO,
            FRAME_LATENCY_MS_HISTO,
            E2E_LATENCY_MS_HISTO,
            LATE_FRAME_MS_HISTO
        };

        struct FrameInfo {
            FrameInfo();
            ~FrameInfo();

            base::TimeTicks capture_time;
            base::TimeTicks capture_end_time;
            base::TimeTicks encode_end_time;
            bool encoded;
        };

        typedef std::map<CastStat, double> StatsMap;
        typedef std::map<CastStat, linked_ptr<SimpleHistogram>> HistogramMap;
        typedef std::map<RtpTimeTicks, FrameInfo> FrameInfoMap;
        typedef std::map<std::pair<RtpTimeTicks, uint16_t>,
            std::pair<base::TimeTicks, CastLoggingEvent>>
            PacketEventTimeMap;
        typedef std::map<CastLoggingEvent, FrameLogStats> FrameStatsMap;
        typedef std::map<CastLoggingEvent, PacketLogStats> PacketStatsMap;

        static const char* CastStatToString(CastStat stat);

        void InitHistograms();

        // Assigns |stats_map| with stats data. Used for testing.
        void GetStatsInternal(StatsMap* stats_map) const;

        // Return a histogram of the type specified.
        SimpleHistogram* GetHistogramForTesting(CastStat stats) const;

        void UpdateFirstLastEventTime(base::TimeTicks timestamp,
            bool is_receiver_event);
        bool GetReceiverOffset(base::TimeDelta* offset);
        void MaybeInsertFrameInfo(RtpTimeTicks rtp_timestamp,
            const FrameInfo& frame_info);
        void RecordFrameCaptureTime(const FrameEvent& frame_event);
        void RecordCaptureLatency(const FrameEvent& frame_event);
        void RecordEncodeLatency(const FrameEvent& frame_event);
        void RecordFrameTxLatency(const FrameEvent& frame_event);
        void RecordE2ELatency(const FrameEvent& frame_event);
        void RecordPacketSentTime(const PacketEvent& packet_event);
        void ErasePacketSentTime(const PacketEvent& packet_event);
        void RecordPacketRelatedLatencies(const PacketEvent& packet_event);
        void UpdateLastResponseTime(base::TimeTicks receiver_time);

        void PopulateFpsStat(base::TimeTicks now,
            CastLoggingEvent event,
            CastStat stat,
            StatsMap* stats_map) const;
        void PopulateFrameCountStat(CastLoggingEvent event,
            CastStat stat,
            StatsMap* stats_map) const;
        void PopulatePacketCountStat(CastLoggingEvent event,
            CastStat stat,
            StatsMap* stats_map) const;
        void PopulateFrameBitrateStat(base::TimeTicks now, StatsMap* stats_map) const;
        void PopulatePacketBitrateStat(base::TimeTicks now,
            CastLoggingEvent event,
            CastStat stat,
            StatsMap* stats_map) const;

        const EventMediaType event_media_type_;

        // Not owned by this class.
        base::TickClock* const clock_;

        // Not owned by this class.
        ReceiverTimeOffsetEstimator* const offset_estimator_;

        FrameStatsMap frame_stats_;
        PacketStatsMap packet_stats_;

        base::TimeDelta total_capture_latency_;
        int capture_latency_datapoints_;
        base::TimeDelta total_encode_time_;
        int encode_time_datapoints_;
        base::TimeDelta total_queueing_latency_;
        int queueing_latency_datapoints_;
        base::TimeDelta total_network_latency_;
        int network_latency_datapoints_;
        base::TimeDelta total_packet_latency_;
        int packet_latency_datapoints_;
        base::TimeDelta total_frame_latency_;
        int frame_latency_datapoints_;
        base::TimeDelta total_e2e_latency_;
        int e2e_latency_datapoints_;

        base::TimeTicks last_response_received_time_;

        int num_frames_dropped_by_encoder_;
        int num_frames_late_;

        // Fixed size map to record when recent frames were captured and other info.
        FrameInfoMap recent_frame_infos_;

        // Fixed size map to record when recent packets were sent.
        PacketEventTimeMap packet_sent_times_;

        // Sender time assigned on creation and |Reset()|.
        base::TimeTicks start_time_;
        base::TimeTicks first_event_time_;
        base::TimeTicks last_event_time_;

        HistogramMap histograms_;

        base::ThreadChecker thread_checker_;
        DISALLOW_COPY_AND_ASSIGN(StatsEventSubscriber);
    };

} // namespace cast
} // namespace media

#endif // MEDIA_CAST_LOGGING_STATS_EVENT_SUBSCRIBER_H_
